package com.meta.nd.service.filter.bo;

import com.meta.common.ConstDef;
import com.meta.nd.filter.vo.FilterFd;
import com.meta.nd.filter.vo.ResultFd;
import com.meta.nd.util.NdDbMapping;
import com.meta.nd.vo.Nd;
import lombok.Data;


import java.util.ArrayList;
import java.util.List;


@Data
public class TableTree implements Cloneable {
    Nd nd;
    NdDbMapping dbMapping;
    List<FilterFd> filterFds = new ArrayList<>();
    List<ResultFd> resultFds = new ArrayList();
    TableTree parent;
    List<TableTree> children = new ArrayList<>();
    boolean isReverse;
    String nvTableName;
    String fvTableName;

    private boolean isRoot(){
        return this.getParent() == null;
    }
    public boolean isReturn(){
        return this.resultFds.size() > 0 || isRoot();
    }

    public boolean isCondition(){
        return this.filterFds.size() > 0;
    }

    public boolean hasDescendantReturnFd(){
        boolean result = false;
        for( TableTree child : this.children){
            if ( child.isReturn() || child.hasDescendantReturnFd()) {
                result = true;
                break;
            }
        }
        return result;
    }

public String getCurrentWhere(){
    if ( !this.isCondition())
        return null;
    StringBuffer sb = new StringBuffer();
    for( FilterFd filterFd : this.filterFds ){
        if ( sb.length() != 0){
            sb.append(" AND ");
        }
        if ( !this.dbMapping.isFvField(filterFd.getFdName())) {
            if ( filterFd.isExpression())
                sb.append(String.format("(%s)",filterFd.getExpression()));
            else
                sb.append(String.format("%s.%s %s :%s", this.nvTableName, filterFd.getFdName(), getLogicalOperator(filterFd.getLogicalOperator()), filterFd.getFdName()));
        }
        else {
            if ( filterFd.isExpression())
                sb.append(String.format("(%s)",filterFd.getExpression()));
            else
                sb.append(String.format("%s.%s %s :%s", this.getFvTableAlias(filterFd.getFinalName()), NdDbMapping.getFvDbNameByDataType(filterFd.getDataType()), getLogicalOperator(filterFd.getLogicalOperator()),filterFd.getFdName()));
        }
    }
    return sb.toString();
}

private String getLogicalOperator(String logicalOperator){
    switch ( logicalOperator ){
        case ConstDef.LOGIC_OPERATION_EQUAL:
            return "=";
        case ConstDef.LOGIC_OPERATION_BIG:
            return ">";
        case ConstDef.LOGIC_OPERATION_BIG_EQUAL:
            return ">=";
        case ConstDef.LOGIC_OPERATION_LESS:
            return "<";
        case ConstDef.LOGIC_OPERATION_LESS_EQUAL:
            return "<=";
        case ConstDef.LOGIC_OPERATION_LEFT_LIKE :
            return "like";
        case ConstDef.LOGIC_OPERATION_RIGHT_LIKE:
            return "like";
        case ConstDef.LOGIC_OPERATION_DOUBLE_LIKE:
            return "like";
        default:
            throw new RuntimeException("不支持的逻辑运算符:" + logicalOperator);
    }
}

public String getUnionChildWhere( TableTree childTree){
    if ( childTree.isReverse )
        return this.getNvTableName() + ".parentId =" + childTree.getNvTableName() + ".id";
    else
        return this.getNvTableName() + ".id =" + childTree.getNvTableName() + ".parentId";
}

    public String getFvJoinsWhere( String fdName){
        return this.getNvTableName() + ".id = " +  this.getNd().getNdName() +  fdName + ".nvId"
                + " and " + this.getNd().getNdName() +  fdName + ".name = '" + fdName + "'";
    }
    public void setParentIdAsOnlyReturnFd(){
        this.clearReturnFd();
        ResultFd returnFd = new ResultFd(ConstDef.FD_NAME_PARENT_ID,null,this.nd.getNdName() );
        this.resultFds.add(returnFd);
    }

    public void setIdAsOnlyReturnFd(){
        this.clearReturnFd();
        ResultFd returnFd = new ResultFd(ConstDef.FD_NAME_ID,null,this.nd.getNdName() );
        this.resultFds.add(returnFd);
    }

    public void clearReturnFd(){
        this.resultFds.clear();
        for( TableTree child : this.children)
            child.clearReturnFd();
    }

    public void addChild(TableTree child){
        this.children.add(child);
        child.setParent(this);
    }

    @Override
    public TableTree clone(){
        try {
            TableTree tableTree = (TableTree) super.clone();
            tableTree.children = new ArrayList<>();
            tableTree.parent = null;
            for(TableTree child : this.children)
                tableTree.addChild(child.clone());
            return tableTree;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public TableTree getTreeNodeByNdCode(String ndCode){
        if ( this.nd.getNdCode().equals(ndCode))
            return this;
        for( TableTree child : children){
            TableTree childTree = child.getTreeNodeByNdCode(ndCode);
            if ( childTree!=null) return childTree;
        }
        return null;
    }

    public TableTree getTreeNodeByNdName(String ndName){
        if ( this.nd.getNdName().equals(ndName))
            return this;
        for( TableTree child : children){
            TableTree childTree = child.getTreeNodeByNdName(ndName);
            if ( childTree!=null) return childTree;
        }
        return null;
    }

    public String getFvTableNameForFdName(String fdName){
        String fvTableName = this.dbMapping.getFvTableName() + " " + this.getFvTableAlias(fdName);
        return fvTableName;
    }

    public String getFvTableAlias(String fdName){
        return this.nd.getNdName() + fdName;
    }

    public List<String> getFvJoinTableNames(){
        List<String> result = new ArrayList<>();
        List<String> fieldNames = new ArrayList<>();
        if(this.isReturn()) {
            for( ResultFd resultFd : this.resultFds ){
                if ( dbMapping.isFvField(resultFd.getFdName())){
                    String fvTableName = getFvTableNameForFdName(resultFd.getFdName());
                    result.add(fvTableName);
                    fieldNames.add(resultFd.getFdName());
                }
            }
            if( this.isCondition() ){
                for(FilterFd filterFd : this.filterFds){
                    if ( !fieldNames.contains(filterFd.getFdName()) && dbMapping.isFvField(filterFd.getFdName())){
                        String fvTableName = getFvTableNameForFdName(filterFd.getFdName());
                        result.add(fvTableName);
                    }
                }
            }
        }
        else if ( this.isCondition()){

        }
        return result;
    }

    public List<String> getResultFdNames(){
        List<String> result = new ArrayList<>();
        if(this.isReturn()) {
            for( ResultFd resultFd : this.resultFds ){
                if (resultFd.isExpression()){
                    result.add( resultFd.getExpression() + " " + resultFd.getFinalName());
                }
                else if ( dbMapping.isFvField(resultFd.getFdName())){
                    String fvTableName = this.nd.getNdName() + resultFd.getFdName()  + "."  + NdDbMapping.getFvDbNameByDataType(resultFd.getDataType()) + " " + resultFd.getFinalName();
                    result.add(fvTableName);
                }
                else {
                    String fvTableName = this.dbMapping.getNvTableName()  + "."  + resultFd.getFdName() + " " + resultFd.getFinalName();
                    result.add(fvTableName);
                }
            }
        }
        return result;
    }

    public List<OrderByInfo> getOrderByInfos(){
        List<OrderByInfo> result = new ArrayList<>();
        if(this.isReturn()) {
            for( ResultFd resultFd : this.resultFds ){
                if (resultFd.getOrderBy() == 0)
                    continue;
                OrderByInfo orderByInfo = new OrderByInfo();
                if (resultFd.isExpression()){
                    orderByInfo.setFdName(resultFd.getFinalName());
                    orderByInfo.setSortOrder(resultFd.getOrderBy());
                }
                else if ( dbMapping.isFvField(resultFd.getFdName())){
                    orderByInfo.setFdName(resultFd.getFinalName());
                    orderByInfo.setSortOrder(resultFd.getOrderBy());
                }
                else {
                    orderByInfo.setFdName(resultFd.getFinalName());
                    orderByInfo.setSortOrder(resultFd.getOrderBy());
                }
                result.add(orderByInfo);
            }
        }
        return result;
    }

    public List<String> getFvJoinFdNames(){
        List<String> result = new ArrayList<>();
        if(this.isReturn()) {
            for( ResultFd resultFd : this.resultFds ){
                if ( dbMapping.isFvField(resultFd.getFdName())){
                    result.add(resultFd.getFdName());
                }
            }
            if( this.isCondition() ){
                for( FilterFd filterFd : this.filterFds){
                    if ( !result.contains(filterFd.getFdName()) && dbMapping.isFvField(filterFd.getFdName())){
                        result.add(filterFd.getFdName());
                    }
                }
            }
        }
        return result;
    }

}
